/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or https://opensource.org/licenses/CDDL-1.0.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */


#include <errno.h>
#include <fcntl.h>
#include <libintl.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <math.h>
#include <sys/stat.h>
#include <sys/mnttab.h>
#include <sys/mntent.h>
#include <sys/types.h>
#include <sys/wait.h>

#include <libzfs.h>
#include <libzfs_core.h>

#include "libzfs_impl.h"
#include "zfs_prop.h"
#include <libzutil.h>

#define	ZDIFF_SHARESDIR		"/.zfs/shares/"

/* stolen from libc/gen/port/gen/execvp.c */
static const char *
execat(const char *s1, const char *s2, char *si)
{
        char    *s;
        int cnt = PATH_MAX + 1; /* number of characters in s2 */

	s = si;
	while (*s1 && *s1 != ':') {
		if (cnt > 0) {
			*s++ = *s1++;
			cnt--;
		} else
			s1++;
	}
	if (si != s && cnt > 0) {
		*s++ = '/';
		cnt--;
	}
	while (*s2 && cnt > 0) {
		*s++ = *s2++;
		cnt--;
	}
	*s = '\0';
	return (*s1 ? ++s1: 0);
}

/*
 * Similiar to execvp(), execpt you can supply an environment and we always
 * use /bin/sh for shell scripts.  The PATH searched is the PATH in the
 * current environment, not the environment in the argument list.
 * This was pretty much stolen from libc/gen/port/execvp.c
 */
int
execvpe(const char *name, char *const argv[], char *const envp[])
{
	const char *path;
	char fname[PATH_MAX+2];
	char *newargs[256];
	int i;
	const char *cp;
	unsigned etxtbsy = 1;
	int eacces = 0;

	if (*name == '\0') {
		errno = ENOENT;
		return (-1);
	}

	if ((path = getenv("PATH")) == NULL)
		path = "/usr/bin:/bin";

	cp = strchr(name, '/')? (const char *)"": path;

	do {
		cp = execat(cp, name, fname);
	retry:
		/*
		 * 4025035 and 4038378
		 * if a filename begins with a "-" prepend "./" so that
		 * the shell can't interpret it as an option
		 */
		if (*fname == '-') {
			size_t size = strlen(fname) + 1;
			if ((size + 2) > sizeof (fname)) {
				errno = E2BIG;
				return (-1);
			}
			(void) memmove(fname + 2, fname, size);
			fname[0] = '.';
			fname[1] = '/';
		}
		(void) execve(fname, argv, envp);
		switch (errno) {
		case ENOEXEC:
			newargs[0] = (char *)"sh";
			newargs[1] = fname;
			for (i = 1; (newargs[i + 1] = argv[i]) != NULL; ++i) {
				if (i >= 254) {
					errno = E2BIG;
					return (-1);
				}
			}
			(void) execve("/bin/sh", newargs, envp);
			return (-1);
		case ETXTBSY:
			if (++etxtbsy > 5)
				return (-1);
			(void) sleep(etxtbsy);
			goto retry;
		case EACCES:
			++eacces;
			break;
		case ENOMEM:
		case E2BIG:
		case EFAULT:
			return (-1);
		}
	} while (cp);
	if (eacces)
		errno = EACCES;
	else
		errno = ENOENT;

	return (-1);
}

int
zfs_ioctl(libzfs_handle_t *hdl, int request, zfs_cmd_t *zc)
{
	return (ioctl(hdl->libzfs_fd, request, zc));
}

const char *
libzfs_error_init(int error)
{
	switch (error) {
	case ENXIO:
		return (dgettext(TEXT_DOMAIN, "The ZFS modules are not "
		    "loaded.\nTry running '/sbin/modprobe zfs' as root "
		    "to load them."));
	case ENOENT:
		return (dgettext(TEXT_DOMAIN, "/dev/zfs and /proc/self/mounts "
		    "are required.\nTry running 'udevadm trigger' and 'mount "
		    "-t proc proc /proc' as root."));
	case ENOEXEC:
		return (dgettext(TEXT_DOMAIN, "The ZFS modules cannot be "
		    "auto-loaded.\nTry running '/sbin/modprobe zfs' as "
		    "root to manually load them."));
	case EACCES:
		return (dgettext(TEXT_DOMAIN, "Permission denied the "
		    "ZFS utilities must be run as root."));
	default:
		return (dgettext(TEXT_DOMAIN, "Failed to initialize the "
		    "libzfs library."));
	}
}

int
libzfs_load_module(void)
{
	return (0);
}

int
find_shares_object(differ_info_t *di)
{
	char fullpath[MAXPATHLEN];
	struct stat64 sb = { 0 };

	(void) strlcpy(fullpath, di->dsmnt, MAXPATHLEN);
	(void) strlcat(fullpath, ZDIFF_SHARESDIR, MAXPATHLEN);

	if (stat64(fullpath, &sb) != 0) {
		(void) snprintf(di->errbuf, sizeof (di->errbuf),
		    dgettext(TEXT_DOMAIN, "Cannot stat %s"), fullpath);
		return (zfs_error(di->zhp->zfs_hdl, EZFS_DIFF, di->errbuf));
	}

	di->shares = (uint64_t)sb.st_ino;
	return (0);
}

/*
 * FIXME: temporary use version with mnttab file discription
 */
int 
getextmntent2(FILE *fp, const char *path, struct extmnttab *entry,
    struct stat64 *statbuf)
{
//	struct stat64 st;
	boolean_t match = B_FALSE;
	boolean_t need_close;

	if (strlen(path) >= MAXPATHLEN) {
		(void) fprintf(stderr, "invalid object; pathname too long\n");
		return (-1);
	}

	if (stat64(path, statbuf) != 0) {
		(void) fprintf(stderr, "%s: %s\n", path, strerror(errno));
		return (-1);
	}

	if ((need_close = (fp == NULL))) {
		if ((fp = fopen(MNTTAB, "re")) == NULL) {
			(void) fprintf(stderr, "cannot open %s\n", MNTTAB);
			return (-1);
		}
	}

	while (getextmntent(fp, entry, 0) == 0) {
		if (makedevice(entry->mnt_major, entry->mnt_minor) ==
		    statbuf->st_dev) {
			match = B_TRUE;
			break;
		}
	}

	if (need_close)
		(void) fclose(fp);

	if (!match) {
		(void) fprintf(stderr, "cannot find mountpoint for '%s'\n",
		    path);
		return (-1);
	}

/* FIXME: linux version has this check  */
//	if (stat64(entry->mnt_mountp, &st) != 0) {
//		entry->mnt_major = 0;
//		entry->mnt_minor = 0;
//		return (-1);
//	}

	return (0);
}

